package com.smsc.headend.task.engine.executor.impl.deviceconfig;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.enums.errorcode.TaskExecuteErrorCode;
import com.smsc.headend.module.asset.dmo.ComConfigDMO;
import com.smsc.headend.module.asset.dmo.MeterConfigDMO;
import com.smsc.headend.module.asset.dto.TouDataDTO;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.common.entity.DeviceOperation;
import com.smsc.headend.module.data.dmo.DeviceSAPandKeys;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.enums.DictionaryType;
import com.smsc.headend.module.data.enums.OperationGroup;
import com.smsc.headend.module.data.enums.OperationName;
import com.smsc.headend.module.protocol.cosem.util.ByteUtils;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.template.clock.entity.TemplateClockEntity;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskExecutorException;
import com.smsc.headend.task.engine.executor.TaskExecutor;
import com.smsc.headend.task.engine.facade.DataItemFacade;
import com.smsc.headend.task.engine.handler.impl.deviceconfig.SetClockObjectHandler;
import com.smsc.headend.task.engine.service.ConnectorService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.task.TaskPacketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @ClassName SetClockObjectExecutor
 * @Description
 * @Author
 * @Date 2021/11/23 10:15
 */
@Slf4j
public class SetClockObjectExecutor implements TaskExecutor {

    @Autowired
    TaskDAO taskDAO;
    @Autowired
    AssetService assetService;
    @Autowired
    FeignAssetManagementService feignAssetManagementService;
    @Autowired
    DataItemFacade dataItemFacade;
    @Autowired
    TaskPacketService packetService;
    @Autowired
    ConnectorService connectorService;

    @Override
    public AtomicTaskState execute(Task task, AtomicTask atomicTask) throws UDISTaskExecutorException {
        log.info("execute step set clock object executor");

        Meter meter = getMeter(task, assetService);
        if (Objects.nonNull(task.getMeterId()) && Objects.isNull(meter)) {
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.METER_NOT_EXISTS.toString());
        }

        Map<String, Object> paramMap = getParameterMap(task, atomicTask);
        if (!paramMap.containsKey("templateClockEntity")) {
            throw new UDISTaskExecutorException("no templateClockEntity");
        }

        ComConfigDMO comConfigDMO = assetService.getComConfigById(task.getComId());
        DeviceOperation dictionary = assetService.getDictionary(atomicTask.getOperationId(), DictionaryType.Operation.getType());
        List<DataItemDTO> dataItemDTOList = dataItemFacade.getDataItemsByOperation(meter, null, dictionary, paramMap, comConfigDMO.getModelId());
        if (CollectionUtil.isEmpty(dataItemDTOList)) {
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.DATAITEM_NOT_EXISTS.toString());
        }

        MeterConfigDMO meterConfigDMO = Objects.nonNull(task.getMeterId()) ? feignAssetManagementService.getMeterConfigById(task.getMeterId()) : null;
        DeviceSAPandKeys deviceSAPandKeys = feignAssetManagementService.getMeterCommunicationKey(meter.getMeterId(), dataItemDTOList.get(0).getRoleId());
        TemplateClockEntity templateClockEntity = JSONUtil.toBean(paramMap.get("templateClockEntity").toString(), TemplateClockEntity.class);
        log.info("templateClockEntity {}", templateClockEntity);

        byte[] packets = getPacketByOperation(atomicTask, meterConfigDMO, dataItemDTOList, meter, deviceSAPandKeys, templateClockEntity);
        if (Objects.isNull(packets)) {
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.PACKETS_IS_NULL.toString());
        }
        log.info(ByteUtils.byteToHexString(packets));
        connectorService.sendPacket(task, atomicTask, dataItemDTOList.get(0).getRoleId(), task.getIsODR(), packets);

        return createAtomicState(SetClockObjectHandler.class);
    }

    private byte[] getPacketByOperation(AtomicTask atomicTask, MeterConfigDMO meterConfigDMO, List<DataItemDTO> dataItemDTOList,
                                        Meter meter, DeviceSAPandKeys deviceSAPandKeys, TemplateClockEntity templateClockEntity) throws UDISTaskExecutorException {

        byte[] packet = null;

        OperationName operationName = OperationName.forId(atomicTask.getOperationId());
        switch (operationName) {
            case SetTimeZone:
            case SetDaylightSavingsBegin:
            case SetDaylightSavingsEnd:
            case SetDaylightSavingsDeviation:
            case SetDaylightSavingsEnabled:
                packet = packetService.setClockObjectPacket(dataItemDTOList.get(0), meter, meterConfigDMO, deviceSAPandKeys, templateClockEntity);
                break;
            case SetTimeShiftEventLimit:
                long timeShiftEventLimit = templateClockEntity.getTimeShiftEventLimit().longValue();
                packet = packetService.writeSimpleDataPacket(dataItemDTOList, meter, timeShiftEventLimit, meterConfigDMO);
                break;
            case SetTimeShiftInvalidLimit:
                long timeShiftInvalidLimit = templateClockEntity.getTimeShiftInvalidLimit().longValue();
                packet = packetService.writeSimpleDataPacket(dataItemDTOList, meter, timeShiftInvalidLimit, meterConfigDMO);
                break;
            default:
                log.info("no implement for this operation {}", operationName);
        }

        return packet;
    }

        @Override
    public Boolean handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.info("error {}", message);

        if (atomicTask.getGroupId() == OperationGroup.SetClock.getId().intValue()) {
            String stepOperationName = OperationName.forId(atomicTask.getOperationId()).getName();
            String result = String.format("step %s failed, [%s]", stepOperationName, message);
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), stepOperationName, result, 3600l, false);
        }

        return true;
    }

}
